package com.gupaoedu.vip.pattern.springmvc2.webmvc.servlet;

import com.gupaoedu.vip.pattern.springmvc1.annotation.MyController;
import com.gupaoedu.vip.pattern.springmvc1.annotation.MyRequestMapping;
import com.gupaoedu.vip.pattern.springmvc2.beans.config.MyBeanWrapper;
import com.gupaoedu.vip.pattern.springmvc2.context.MyApplicationContext;
import javafx.scene.shape.Path;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class MyDispatcherServlet extends HttpServlet {

    private Logger logger = LoggerFactory.getLogger(MyDispatcherServlet.class);

    private MyApplicationContext applicationContext;

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private List<MyHandlerMapping> handlerMappings = new ArrayList<MyHandlerMapping>();

    private Map<MyHandlerMapping, MyHandlerAdapter> handlerAdapter = new ConcurrentHashMap<MyHandlerMapping, MyHandlerAdapter>();

    private List<MyViewResolver> viewResolvers = new ArrayList<MyViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp){
        try{
            MyHandlerMapping handler = getHandler(req);
            if(handler==null){
                //返回404
                processDispatchResult(req,resp,new MyModelAndView("404"));
                return;
            }
            MyHandlerAdapter ha = getHandlerAdapter(handler);

            Object mv = ha.handle(req, resp, handler);
            boolean isModelAndView = handler.getMethod().getReturnType() == MyModelAndView.class;
            if(!isModelAndView){
                resp.getWriter().write(String.valueOf(mv));
                return;
            }
            //输出
            processDispatchResult(req, resp, (MyModelAndView) mv);
        }catch (Exception e){
            e.printStackTrace();
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("stackTrace", e.getStackTrace());
            map.put("message", e.getMessage());
            try {
                processDispatchResult(req,resp,new MyModelAndView("500",map));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }

    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, MyModelAndView mv) throws Exception {
        if(null == mv){return;}
        if(this.viewResolvers.isEmpty()){return;}
        for (MyViewResolver viewResolver : this.viewResolvers) {
            MyView view = viewResolver.resolveViewName(mv.getViewName(),null);
            view.render(mv.getModel(),req,resp);
            return;
        }

    }

    private MyHandlerAdapter getHandlerAdapter(MyHandlerMapping handler) {
        if(this.handlerAdapter.isEmpty()){return null;}
        MyHandlerAdapter ha = this.handlerAdapter.get(handler);
        if(ha.supports(handler)){
            return ha;
        }
        return null;
    }

    private MyHandlerMapping getHandler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()){return null;}
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for (MyHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if(!matcher.matches()){continue;}
            return handlerMapping;
        }

        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // TODO 初始化ApplicationContext
        applicationContext = new MyApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        // TODO 初始化springmvc九大组件
        initStrategies(applicationContext);
    }

    //初始化策略
    protected void initStrategies(MyApplicationContext context) {
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);
        //handlerMapping
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //初始化视图转换器
        initViewResolvers(context);
        //
        initFlashMapManager(context);
    }

    private void initFlashMapManager(MyApplicationContext context) {

    }

    private void initViewResolvers(MyApplicationContext context) {

        // 拿到一个模板的存放目录
        String template = context.getConfig().getProperty("template");
        String templatePath = this.getClass().getClassLoader().getResource(template).getFile();
        File templatePathDir = new File(templatePath);
        for (File file : templatePathDir.listFiles()) {
            this.viewResolvers.add(new MyViewResolver(templatePath));
        }


    }

    private void initRequestToViewNameTranslator(MyApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(MyApplicationContext context) {

    }

    private void initHandlerAdapters(MyApplicationContext context) {
        if(this.handlerMappings.isEmpty()){return;}
        for (MyHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapter.put(handlerMapping, new MyHandlerAdapter());
        }


    }

    private void initHandlerMappings(MyApplicationContext context) {
        String [] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            MyBeanWrapper beanWrapper = (MyBeanWrapper) context.getBean(beanName);
            Class<?> clazz = beanWrapper.getWrappedClass();
            if(!clazz.isAnnotationPresent(MyController.class)){continue;}
            String baseUrl = "";
            //取出Controller类的路径
            if(clazz.isAnnotationPresent(MyRequestMapping.class)){
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl += requestMapping.value();
            }
            Method [] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if(!method.isAnnotationPresent(MyRequestMapping.class)){continue;}
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                String url = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(url);
                this.handlerMappings.add(new MyHandlerMapping(beanWrapper.getWrappedInstance(), method, pattern));
                logger.info("Mapped "+url+","+method);
            }

        }

    }

    private void initThemeResolver(MyApplicationContext context) {
    }

    private void initLocaleResolver(MyApplicationContext context) {
        
    }

    private void initMultipartResolver(MyApplicationContext context) {
    }
}
